/*
 * @Author: c
 * @Date: 2023-12-06 01:06:29
 * @LastEditTime: 2024-01-20 02:40:29
 * @LastEditors: c
 * @Description: 
 * @FilePath: \music-web-backend\src\core\system\roles\roles.service.ts
 * https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { Injectable } from '@nestjs/common';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Role } from './entities/role.entity';
import { Brackets, DataSource, Repository, WhereExpressionBuilder } from 'typeorm';
import { GenderEnum } from 'src/common/enums/gender.enum';
import { RoleBaseVo, RolePageQuery, RolePageVo } from './dto/search-role.dto';
import { isNumber } from 'lodash';
import { PageResDto } from 'src/common/dto/page.dto';
import { User } from '../user/entities/user.entity';
import { plainToClass } from 'class-transformer';
import { CommonException } from 'src/common/exception/common.exception';
import { EXCEPTION } from 'src/common/const/constants';
import { IOption } from 'src/common/dto/option.dto';
import { RoleMenu } from './entities/role_menu.entity';

@Injectable()
export class RolesService {




  constructor(
    @InjectRepository(Role) private roleRepository: Repository<Role>,
    @InjectRepository(RoleMenu) private roleMenuRepository: Repository<RoleMenu>,

    private readonly dataSource: DataSource
  ) { }

  /**
   * 角色-重新分配菜单
   * @param roleId 角色ID
   * @param menuIds 菜单ID集合
   */
  async updateRoleMenus(roleId: number, menuIds: number[]) {

    //新建一个事务runner
    const queryRunner = await this.dataSource.createQueryRunner()
    //连接runner
    queryRunner.connect()
    //开启事务
    await queryRunner.startTransaction()

    try {

      //删除旧的角色菜单
      await queryRunner
        .manager
        .createQueryBuilder()
        .delete()
        .from(RoleMenu)
        .where('role_menu.role_id = :roleId', { roleId })
        .execute()

      const roleMenus: RoleMenu[] = menuIds.map(menuId => ({ roleId, menuId }))
      //新增角色菜单
      if (menuIds.length > 0) {
        await queryRunner
          .manager
          .createQueryBuilder()
          .insert()
          .into(RoleMenu)
          .values(roleMenus)
          .execute()
      }

      //提交事务
      await queryRunner.commitTransaction()
    } catch (error) {
      //回滚事务
      await queryRunner.rollbackTransaction()
      throw new CommonException(EXCEPTION.COMMON);
    } finally {
      //释放runner的连接
      await queryRunner.release()
    }

  }


  /**
   * 获取某角色所属的菜单id列表
   */
  async getRoleMenuIds(roleId: number): Promise<number[]> {
    const menuIds = await this.roleRepository.createQueryBuilder('role').select('menu.id as menuId')
      .innerJoin("role.menus", 'menu')
      .where('role.id = :roleId', { roleId }).getRawMany()

    let res = []
    if (menuIds.length > 0) {
      res = menuIds.map(menu => +menu.menuId)
    }
    return res
  }

  /**
   * 字典下拉列表
   * @returns 
   */
  async listDictOptions(): Promise<IOption<string, number>[]> {
    const dictList: Role[] = await this.roleRepository.find()
    return dictList.map(r => ({ label: r.name, value: r.id }))
  }

  /**
   * 角色-新建
   * @param createRoleDto 
   * @returns 
   */
  async create(createRoleDto: CreateRoleDto) {
    const one = await this.roleRepository.findOne({ where: { name: createRoleDto.name } })
    if (one) {
      throw new CommonException(EXCEPTION.EXIST)
    }

    return await this.roleRepository.save(plainToClass(Role, createRoleDto));
  }

  /**
   * 角色-查询
   * @param user jwt解析的当前用户
   * @param param1 分页字段，关键字
   * @returns 
   */
  async findAll(user: User, { pageNum = 1, pageSize = 10, keywords }: RolePageQuery): Promise<PageResDto<RolePageVo>> {
    // const [users, count] = await this.userRepository.findAndCount({
    //   where:
    //   [{
    //     username: Not('root'),
    //     deleted: Equal(0),
    //     createTime: Between(new Date(startTime),new Date(endTime)),
    //   }],
    //   relations: { roles: true }, skip: (pageNum - 1) * pageSize, take: pageSize
    // });

    const builder = this.roleRepository.createQueryBuilder('role')
      .andWhere('role.deleted = :deleted', { deleted: 0 }).printSql();

    const roleCodes: any[] = user.roles
    if (roleCodes.indexOf('ROOT') === -1) {
      builder.andWhere('role.code != :code', { code: 'ROOT' })
    }

    if (keywords) {
      builder.andWhere(new Brackets((qb: WhereExpressionBuilder) => {
        qb.where('role.name like :keywords', { keywords: `%${keywords}%` })
          .orWhere('role.code like :keywords', { keywords: `%${keywords}%` })

      }))
    }

    const total = await builder.getCount()
    const roles: Role[] = await builder.offset((pageNum - 1) * pageSize).limit(pageSize).getMany();

    const usersPageDto: RolePageVo[] = plainToClass(RolePageVo, roles);


    return { list: usersPageDto, total: total }
  }

  /**
   * 角色-根据id查询单个记录
   * @param id 角色 id
   * @returns 
   */
  async findOne(id: number) {
    const data = await this.roleRepository.findOne({
      where: {
        id
      }
    });
    const list = plainToClass(RoleBaseVo, data)
    return list
  }


  /**
   * 角色-根据name查询 单个记录
   * @param name 角色-name 字段
   * @returns 
   */
  async findOneByName(name: string) {
    const data = await this.roleRepository.findOne({
      where: {
        name
      }
    });

    return data
  }

  /**
   * 角色-修改
   * @param id 角色ID
   * @param updateRoleDto 需要修改的角色数据 
   */
  async update(id: number, updateRoleDto: UpdateRoleDto) {

    const role: Role = plainToClass(Role, updateRoleDto)

    //修改用户
    const updateRes = await this.roleRepository
      .createQueryBuilder()
      .update(Role)
      .set(role)
      .where('id = :id', { id: id })
      .execute()
  }

  /**
   * 角色-根据id集合-批量删除
   * @param ids 角色id字符串，以“,”分割
   */
  async remove(ids: string) {
    const idList = ids.split(',')


    // create a new query runner
    // establish real database connection using our new query runner
    const queryRunner = this.dataSource.createQueryRunner()

    queryRunner.connect()
    // lets now open a new transaction:
    await queryRunner.startTransaction()

    // execute some operations on this transaction:
    try {
      for await (const id of idList) {
        queryRunner.manager
          .createQueryBuilder()
          .delete()
          .from(Role)
          .where("id = :id", { id })
          .execute();
        // this.roleRepository.delete(id)
      }

      // commit transaction now:
      await queryRunner.commitTransaction()
    } catch (error) {
      // since we have errors let's rollback changes we made
      await queryRunner.rollbackTransaction()
      throw new CommonException(EXCEPTION.COMMON)
    } finally {
      // you need to release query runner which is manually created:
      await queryRunner.release()
    }
  }
}
